#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include "cc_api/cc_api.h"
#include "config.h"
#include "utils.h"
#include "cc_api/cc_device.h"
#include "zbar.h"
#include "png.h"


#define MAXFRAMELEN  0x40000

typedef enum {
    CC_RAW_SOURCE_H264,
    CC_RAW_SOURCE_H265,
} ccrawSource;

typedef struct stream_s {
  pthread_t pid;
  bool bcontinue;
  ccrawSource source;
} stream_t;

enum ccctreamType {
    CCA_STREAM_AUDIO = 1,
    CCA_STREAM_VIDEO,
    CCA_STREAM_AUDIOCONFIG,
    CCA_STREAM_VIDEOCONFIG,
    CCA_STREAM_MAX,
};

typedef struct
{
    cc_uint64 utcms;
    cc_uint32 pts;
    cc_uint32 seq;
}FrameInfo_t;

typedef struct 
{
    unsigned char type;   // 1:audio   2:video   3:audiocfg   4:videocfg
    unsigned char sync;  //1:关键帧   0:非关键帧
    unsigned short timespan;
    unsigned int datasize;
    unsigned int starttime;  //时间戳
    unsigned int seq;
}LocalFrameHead_t;


static stream_t g_stream;
static cc_wifi_s g_wifi_list;
static cc_callback device_callbk;
static int g_support_fisheye = 0;
static int g_keeplive = 0;

static void demo_wakeup(cc_wakeup_type_e type);


/************************************************************************
* 用于二维码解析，此示例代码将png图片转为YUV数据，设备接入时直接将采集的
* YUV数据接入即可
*************************************************************************/
static void get_data (int *width, int *height,void **raw)
{   
#if 0
    FILE *file = fopen("/tmp/18260233093.png", "rb");
    if(!file) exit(1);   
    png_structp png =
        png_create_read_struct(PNG_LIBPNG_VER_STRING,
                               NULL, NULL, NULL);
    if(!png) exit(3);
    if(setjmp(png_jmpbuf(png))) exit(4);
    png_infop info = png_create_info_struct(png);
    if(!info) exit(5);
    png_init_io(png, file);
    png_read_info(png, info);    
    /* configure for 8bpp grayscale input */
    int color = png_get_color_type(png, info);
    int bits = png_get_bit_depth(png, info);
    if(color & PNG_COLOR_TYPE_PALETTE)
        png_set_palette_to_rgb(png);
    if(color == PNG_COLOR_TYPE_GRAY && bits < 8)
        png_set_expand_gray_1_2_4_to_8(png);
    if(bits == 16)
        png_set_strip_16(png);
    if(color & PNG_COLOR_MASK_ALPHA)
        png_set_strip_alpha(png);
    if(color & PNG_COLOR_MASK_COLOR)
        png_set_rgb_to_gray_fixed(png, 1, -1, -1);
    /* allocate image */
    *width = png_get_image_width(png, info);
    *height = png_get_image_height(png, info);
    *raw = malloc(*width * *height);    
    png_bytep rows[*height];
    int i;
    for(i = 0; i < *height; i++)
        rows[i] = *raw + (*width * i);       
    png_read_image(png, rows);    
#endif
}



/*低功耗设备连接心跳保活服务器*/
int connnect_heartbeat_server(cc_hb_config hbconfig, int* psockfd)
{
    struct sockaddr_in servaddr;
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family= AF_INET;
    servaddr.sin_port = htons(hbconfig.serverport[0].port);
    if(inet_pton(AF_INET, hbconfig.serverport[0].server, &servaddr.sin_addr) <= 0)
    {
        printf("inet_pton failed.\n");
        return -1;
    }

    *psockfd = socket(AF_INET,SOCK_STREAM,0);

    if(connect(*psockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    {
        printf("connect failed.\n");
        return -1;
    }
    printf("TCP connected successfully!\n");
    return 0;
}

/*低功耗设备维持和保活服务器的心跳连接*/
int interact_heartbeat_server(cc_hb_config hbconfig, int sockfd)
{
    fd_set rdset;
    fd_set wtset;
    struct timeval tv;
    int rlen;
    int ret = 0;
    uint64_t tnow = 0;
    char buffer[64] = {0};
    printf("enter into heartbeat loop.\n");

    while (g_keeplive)
    {
        tv.tv_sec = 1;
        tv.tv_usec = 0;

        FD_ZERO(&rdset);
        FD_ZERO(&wtset);
        FD_SET(sockfd, &rdset);
        
        if (tnow == 0 || util_current_utc_time_ms() - tnow >= hbconfig.interval * 1000)
        {
            tnow = util_current_utc_time_ms();
            printf("to send heartbeat request.\n");    //发送心跳包
            if (write(sockfd, hbconfig.heartbeatrequest, hbconfig.heartbeatrequestlen)
                    != hbconfig.heartbeatrequestlen)
                {
                    printf("TCP write error\n");
                    return -1;
                }
        }
        
        ret = select(sockfd+1, &rdset, NULL, NULL, &tv);
        if(ret == -1)
        {
            printf("TCP select error %s\n", strerror(errno));
            return -1;
        }
        else if(ret == 0)
        {
            printf("keep live...\n");
            continue;
        }

        if(FD_ISSET(sockfd, &rdset))
        {
_ReadAgain:
            rlen = read(sockfd, buffer, hbconfig.wakeuprequestlen);
            if (rlen > 0)
            {
                printf("TCP read %dB from server.\n", rlen);     //匹配收到的tcp包的报文
                if (rlen == hbconfig.wakeuprequestlen 
                	&& memcmp(buffer, hbconfig.wakeuprequest, hbconfig.wakeuprequestlen) == 0)
                {
                    printf("recv wakeup request\n", buffer);     //收到唤醒包
                    return 0;
                }
                else
                {
                    printf("read other: %s\n", buffer);
                }
            }
            else if (rlen == 0)
            {
                printf("TCP read zero return!\n");
                return -1;
            }
            else
            {
                if (EINTR == errno)
                {
                    goto _ReadAgain;
                }
                else if (EWOULDBLOCK == errno || EAGAIN == errno)
                {
                    usleep(200);
                    continue;
                }
                else
                {
                    printf("TCP other error of socket read, %d!\n", errno);
                    return -1;
                }
            }
        }        
    }
    
    return -1;
}


/*启动低功耗设备和保活服务器的连接*/
static void *demo_keep_live_pro(void* args)
{
    cc_hb_config config = *(cc_hb_config*)(args);

    printf("heartbeat pkg len:%d, wakeup pkg len:%d\n", config.heartbeatrequestlen, config.wakeuprequestlen);
    printf("heartbeat pkg interval:%d\n",config.interval);
    int i=0;
    for(;i<config.servernum;i++)    //如果有多个服务器，设备要和每个服务器都建立TCP连接
    {
        printf("keep live server:%s:%d\n",config.serverport[i].server, config.serverport[i].port);
    }
    int sockfd=-1;

    while (g_keeplive)
    {
        if (connnect_heartbeat_server(config, &sockfd) == 0)
        {
            if(interact_heartbeat_server(config, sockfd) == 0)
            {
                printf("receive remove wakeup request\n");
                //收到唤醒请求，主控板上电，启动SDK
                demo_wakeup(CC_WAKE_UP_BY_APP_CLIENT);
                break;
            }
        }
        if (sockfd != -1)
        {
            close(sockfd);
            sockfd = -1;
        }
        sleep(1);
        printf("loop again\n");
    }
    if (sockfd != -1)
    {
        close(sockfd);
        sockfd = -1;
    }
    printf("end loop\n");
    return NULL;
}


/*低功耗wifi芯片建立和保活服务器的连接，进入休眠状态*/
static int demo_start_sleep(cc_hb_config* config)
{
    static cc_hb_config scfg;
    memcpy(&scfg,config,sizeof(scfg));
    printf("Begin___\n");
    pthread_t pid;
    g_keeplive = 1;
    pthread_create(&pid, NULL, demo_keep_live_pro, &scfg);

    pthread_detach(pid);
    printf("End___\n");

    return 0;
}

static void demo_quit_sleep()
{
    printf("quit sleep proc\n");
    g_keeplive = 0;
}


static void demo_sleep()
{
    cc_hb_config config ;
    memset(&config,0,sizeof(config));
    if(0 == cc_api_suspend(&config))   //获取保活服务器信息
    {
        demo_start_sleep(&config);  
    }
    cc_api_stop();                     //主控板下电    
}

static void demo_wakeup(cc_wakeup_type_e type)
{
    //低功耗设备，PIR唤醒，实际使用过程中唤醒操作在启动流程的基础上，调用cc_api_restore

    cc_detect_type_e  event;
    
    demo_quit_sleep();                        //断开和保活服务器的连接（非必须）
    cc_api_restore(type, CC_WAKE_UP_REASON_TYPE_NULL);
    cc_api_start(); 
    if(type == CC_WAKE_UP_BY_RING)
    {
        event = CC_DETECT_RING;
    }
    else if(type == CC_WAKE_UP_BY_PIR)
    {
        event = CC_DETECT_PIR;
    }
    else
    {
        return;
    }
    cc_api_set_event(util_current_utc_time_ms(), event, CC_DETECT_PULSE, NULL);

}


/************************************************************************
* 设备相关的回调函数处理
*************************************************************************/
static int demo_cc_device_cb(cc_device_cmd_e cmd, void* data, void* user_data)
{
    int *value = (int *)data;
    switch(cmd)
    {
         case CC_CMD_LED_SWITCH:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_LED_SWITCH, status %s\n",__FUNCTION__,__LINE__,cmd,*value==1?"on":"off");            
             break;
         }
         case CC_CMD_REBOOT:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_REBOOT\n",__FUNCTION__,__LINE__,cmd);
             break;
         }
         case CC_CMD_PACK_LOGS:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_PACK_LOGS\n",__FUNCTION__,__LINE__,cmd);
             strcpy((char*)data, "/tmp/xxx.log");
             break;
         }
         case CC_CMD_SET_SYSTEM_TIME:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_SET_SYSTEM_TIME, utc time is %lld(ms)\n",__FUNCTION__,__LINE__,cmd, *(cc_uint64*)data);
             break;
         }
         case CC_CMD_GET_BATTERY:
         {
             *value = (int)BATTERY_STATUS_CHARGING;
             printf("%s line(%d) cmd(%d)=CC_CMD_GET_BATTERY battery=%d\n",__FUNCTION__,__LINE__,cmd, *value);
             break;
         }
         case CC_CMD_GET_BATTERY_PERCENT:
         {
             *value = 50;
             printf("%s line(%d) cmd(%d)=CC_CMD_GET_BATTERY battery percent=%d\n",__FUNCTION__,__LINE__,cmd, *value);
             break;
         }
         case CC_CMD_SET_PIR_STATUS:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_SET_PIR_STATUS status=%s\n",__FUNCTION__,__LINE__,cmd,*value==1?"on":"off");
             break;
         }
         case CC_CMD_DEV_INACTIVE:         //建议进入休眠，设备收到此回调时，需要结合当前设备的状态判断是否需要进入休眠，比如在升级过程中是不能休眠的
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_DEV_INACTIVE camera will sleep\n",__FUNCTION__,__LINE__,cmd);
             demo_sleep();
             break;
         }         
         case CC_CMD_QR_START:
         {
             do {
                 zbar_image_scanner_t *scanner = NULL;
                 scanner = zbar_image_scanner_create();
                 zbar_image_scanner_set_config(scanner, 0, ZBAR_CFG_ENABLE, 1);
 
                 int width = 0, height = 0;
                 void *raw = NULL;
                 cc_qrcode_info *qdata = (cc_qrcode_info*)data;
                 get_data(&width, &height, &raw);                
                 qdata->width = width;
                 qdata->height = height;
                 qdata->pdata = (char *)malloc(width * height);
                 memcpy(qdata->pdata ,raw, width * height);
             } while(0);
             break;
         }
         case CC_CMD_QR_RELEASE:
         {

         	if(data) {
            	cc_qrcode_info *qdata = (cc_qrcode_info*)data;
                if(qdata->pdata) {
                	free(qdata->pdata);
                    qdata->pdata = NULL;
                }
             }
             break;
         }
         case CC_CMD_QR_STOP:
         {
            if(data) {
                cc_qrcode_info *qdata = (cc_qrcode_info*)data;
                if(qdata->pdata) {
                    free(qdata->pdata);
                    qdata->pdata = NULL;
                }
            }
            break;
         }         
		 case CC_CMD_QR_DATA_START:  //传入扫描到的二维码字符串
         {
             do {
                printf("CC_CMD_QR_DATA_START\n");
                char *qrcode = ":6\n6l;j=g:g7d=\nwdhGDcN7^\nUJjIy\nv,?Rex-@g\n5Pm5H$#iP";                 
                 memcpy((char *)data ,qrcode, strlen(qrcode));
             } while(0);
             break;
         }
         case CC_CMD_QR_DATA_RELEASE:
         {
            printf("CC_CMD_QR_DATA_RELEASE\n");
            break;
         }
         case CC_CMD_QR_DATA_STOP:
         {            
            printf("CC_CMD_QR_DATA_STOP\n");
            break;
         } 
         case CC_CMD_SET_PIR_SENSITIVITY:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_SET_PIR_SENSITIVITY, value=%d\n",__FUNCTION__,__LINE__,cmd, *value);
             break;
         }
         case CC_CMD_SET_PRIVATE_MODE:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_SET_PRIVATE_MODE, value=%d\n",__FUNCTION__,__LINE__,cmd, *value);
             break;
         }
         case CC_CMD_STREAM_RATELIMIT_STATUS:
         {
            printf("%s line(%d) cmd(%d)=CC_CMD_STREAM_RATELIMIT_STATUS, video bps %s\n",__FUNCTION__,__LINE__,cmd, *value?"normal":"abnormal");
            break;
         }
         case CC_CMD_SET_SOUNDTRACKING:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_SET_SOUNDTRACKING, value=%d\n",__FUNCTION__,__LINE__,cmd, *value);
             break;
         }
         case CC_CMD_SET_AUTOTRACKING:
         {
             printf("%s line(%d) cmd(%d)=CC_CMD_SET_AUTOTRACKING, value=%d\n",__FUNCTION__,__LINE__,cmd, *value);
             break;
         }
         case CC_CMD_PLAY_STATUS:
         {
             printf("%s livepreview status:%d\n",__FUNCTION__,  *value);
             break;
         }
         
         default:
            printf("%s: cmd[%d] not support\n",__FUNCTION__, cmd);
            break;
     }

    return 0;
}

/************************************************************************
* 视频相关的回调函数处理
*************************************************************************/
static int demo_cc_video_cb(cc_video_cmd_e cmd, void* data, void* user_data)
{
    int *value = (int *)data;

   switch(cmd)
   {
        case CC_CMD_SET_NIGHT_MODE:
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_NIGHT_MODE mode=%d\n",__FUNCTION__,__LINE__,cmd,*value);
            break;
        case CC_CMD_SET_ANDTIFLICKER:
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_ANDTIFLICKER freq=%d\n",__FUNCTION__,__LINE__,cmd,*value);
            break;
        case CC_CMD_SET_ROTATE:
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_ROTATE angle=%d\n",__FUNCTION__,__LINE__,cmd,*value);
            break;
        case CC_CMD_SET_MOTION_DETECTION_SESSITIVITY:
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_MOTION_DETECTION_SESSITIVITY sensitivity=%d\n",__FUNCTION__,__LINE__,cmd,*value);
            break;
        case CC_CMD_SET_MOTION_DETECTION_REGION:
            {
                printf("%s line(%d) cmd(%d)=CC_CMD_SET_MOTION_DETECTION_REGION\n",__FUNCTION__,__LINE__,cmd);
                cc_rect_s *rectvalue = (cc_rect_s *)data;                           
                int cc_count = rectvalue->regions_num;
                cc_rect *p_rect = NULL;
                int i = 0; 
                for (i = 0; i < cc_count; ++i) 
                {
                    p_rect = rectvalue->rect + i;             
                    printf("id=%d left=%d top=%d right=%d bottom=%d\n", i, p_rect->left, p_rect->top,p_rect->right,p_rect->bottom);
                }
                break;
            }
        case CC_CMD_SET_HD_VIDEO:
        {
            cc_hd_s hd;
            memcpy(&hd,data,sizeof(hd));
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_HD_VIDEO streamid=%d, hd=%d\n",__FUNCTION__,__LINE__,cmd,hd.streamid,hd.hd);
            break;
        }

        default:
            printf("%s cmd[%d] not legality\n",__FUNCTION__, cmd);
   }

    return 0;
}

/************************************************************************
* 声音相关的回调函数处理
*************************************************************************/
static int demo_cc_audio_cb(cc_audio_cmd_e cmd, void* data, void* user_data)
{
    int *value = (int*)data;

   switch(cmd)
   {
        case CC_CMD_AUDIO_START:
            printf("%s line(%d) cmd(%d)=CC_CMD_AUDIO_START\n",__FUNCTION__,__LINE__,cmd);
            break;
        case CC_CMD_AUDIO_STOP:
            printf("%s line(%d) cmd(%d)=CC_CMD_AUDIO_STOP\n",__FUNCTION__,__LINE__,cmd);
            break;
        case CC_CMD_AUDIO_TALK:
            printf("%s line(%d) cmd(%d)=CC_CMD_AUDIO_TALK, len %d\n",__FUNCTION__,__LINE__,cmd, ((cc_buf*)data)->bytes);
            if (((cc_buf*)data)->bytes == 0)
            {
                printf("CC_CMD_AUDIO_TALK stopped!\n");
            }
            break;
        case CC_CMD_SET_SOUND_DETECTION_SENSITIVITY:
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_SOUND_DETECTION_SENSITIVITY sensitivity=%d\n",__FUNCTION__,__LINE__,cmd, *value);
            break;        
        case CC_CMD_SET_MIC:
            printf("%s line(%d) cmd(%d)=CC_CMD_SET_MIC,mute=%d\n",__FUNCTION__,__LINE__,cmd,*value);
            break;
        default:
            printf("%s: cmd[%d] not legality\n",__FUNCTION__, cmd);
   }

    return 0;
}


/************************************************************************
* 网络相关的回调函数处理
*************************************************************************/
static int demo_cc_network_cb(cc_network_cmd_e cmd, void* data, void* user_data)
{
    cc_wifi *wifi_data = NULL;

    switch(cmd)
    {
        case CC_CMD_GET_WIFI_LIST:
            printf("%s line(%d) cmd(%d)=CC_CMD_GET_WIFI_LIST\n",__FUNCTION__,__LINE__,cmd);
            g_wifi_list.count = 2;
            g_wifi_list.wifi_list_buf = malloc(g_wifi_list.count*sizeof(cc_wifi));
            wifi_data = (cc_wifi *)g_wifi_list.wifi_list_buf;
            strcpy(wifi_data[0].bssid,"12345678");
            strcpy(wifi_data[0].ssid,"12345678");
            strcpy(wifi_data[0].passwd,"12345678");
            wifi_data[0].signal = 5;
            wifi_data[0].mode = CC_WIFI_MODE_WPA_WPA2;
            wifi_data[0].noise_level = 1;
            wifi_data[0].signal_level = 1;
            wifi_data[0].connected = 1;

            strcpy(wifi_data[1].bssid,"abcdefgh");
            strcpy(wifi_data[1].ssid,"abcdefgh");
            strcpy(wifi_data[1].passwd,"abcdefgh");
            wifi_data[1].signal = 3;
            wifi_data[1].mode = CC_WIFI_MODE_WPA2;
            wifi_data[1].noise_level = 1;
            wifi_data[1].signal_level = 1;
            wifi_data[1].connected = 0;

            memcpy(data, &g_wifi_list, sizeof(cc_wifi_s));            
            break;

        case CC_CMD_SWITCH_WIFI:
            wifi_data = (cc_wifi*)data;
            printf("%s line(%d) cmd(%d)=CC_CMD_SWITCH_WIFI\n",__FUNCTION__,__LINE__,cmd);
            printf("get wifi ssid=%s,key=%s,mode=%d\n",wifi_data->ssid,wifi_data->passwd,wifi_data->mode);            
            //连接WIFI，连接成功返回0， 如果连接失败，需要返回-1
            break;

        case CC_CMD_GET_WIFI_DEVICE_NAME:
            memcpy(data, "wlan0", strlen("wlan0"));    
            printf("%s line(%d) cmd(%d)=CC_CMD_GET_WIFI_DEVICE_NAME\n",__FUNCTION__,__LINE__,cmd);
            break;

        case CC_CMD_CURRENT_WIFI:
            wifi_data = (cc_wifi*)malloc(sizeof(cc_wifi));
            strcpy(wifi_data->bssid,"20D13");
            strcpy(wifi_data->ssid,"20D15");
            strcpy(wifi_data->passwd,"12345678");
            wifi_data->signal = 5;
            wifi_data->mode = CC_WIFI_MODE_WPA_WPA2;
            wifi_data->noise_level = 1;
            wifi_data->signal_level = 1;
            wifi_data->connected = 1;
            memcpy(data,wifi_data,sizeof(cc_wifi));   
            free(wifi_data);
            wifi_data = NULL;
            break;
        default:
            printf("%s: cmd[%d] not legality\n",__FUNCTION__,cmd);
    }

    return 0;
}

/************************************************************************
* 获取设备能力默认值参数，一般和能力集对应，设备注册时会获取，或者设备升级后有新的能力时获取
*************************************************************************/
static int demo_cc_get_feature_default_value_cb(cc_device_default_setting_type_e type, void* value, int len, void* user_data)
{
    printf("%s: line(%d), type:0x%x.\n", __FUNCTION__, __LINE__, type);
    switch(type)
    {
        case CC_DEFAULT_SETTING_TYPE_NIGHT_VISION_MODE:
        {
            strncpy(value, "2", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_PIR_STATUS:
        {
            strncpy(value, "1", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_MOTION_DETECT_STATUS:
        {
            strncpy(value, "1", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_SOUND_DETECT_STATUS:
        {
            strncpy(value, "1", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_PEOPLE_DETECT_STATUS:
        {
            strncpy(value, "1", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_MOTION_DETECT_SENSITIVITY:
        {
            strncpy(value, "3", len);     
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_SOUND_DETECT_SENSITIVITY:
        {
            strncpy(value, "3", len);    
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_HD_VIDEO:
        {
            strncpy(value, "1", len);   
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_PIR_SENSITIVITY:
        {
            strncpy(value, "50", len); 
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_LED_STATUS:
        {
            strncpy(value, "1", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_ANTIFLICKER:
        {
            strncpy(value, "50", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_AUTOTRACKING:
        {
            strncpy(value, "0", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_FACE_DETECT_STATUS:
        {
            strncpy(value, "1", len);  
            break;
        }
        case CC_DEFAULT_SETTING_TYPE_CAMERAIMAGEROTATE:
        {
            strncpy(value, "0", len);  
            break;
        }     
        case CC_DEFAULT_SETTING_TYPE_SOUNDTRACKING:
        {
            strncpy(value, "1", len);
            break;
        }
        default:
        {
            *(char*)value = 0;
            break;
        }
    }
    return 0;
}

static int demo_cc_get_feature_capacity_cb(cc_device_capacity_e capacity_type, void* value, int len, void* user_data)
{
    printf("%s: line(%d), type:0x%x.\n", __FUNCTION__, __LINE__, capacity_type);    
    return 0;
}

static int demo_cc_server_status_cb(cc_server_status_e server_status,void* data,void* user_data)
{
   switch(server_status)
   {
        case CC_REGISTER_SUCCESS:
            printf("%s line(%d) server_status(%d)=CC_REGISTER_SUCCESS\n",__FUNCTION__,__LINE__,server_status);
            break;
        case CC_REGISTER_FAIL:
            printf("%s line(%d) server_status(%d)=CC_REGISTER_FAIL\n",__FUNCTION__,__LINE__,server_status);
            break;
        case CC_SERVER_STATUS_OFFLINE:
            printf("%s line(%d) server_status(%d)=CC_SERVER_STATUS_OFFLINE\n",__FUNCTION__,__LINE__,server_status);
            break;
        case CC_SERVER_STATUS_ONLINE:
            printf("%s line(%d) server_status(%d)=CC_SERVER_STATUS_ONLINE\n",__FUNCTION__,__LINE__,server_status);
            break;
        case CC_SERVER_STATUS_DEVICE_REMOVED:
            printf("%s line(%d) server_status(%d)=CC_SERVER_STATUS_DEVICE_REMOVED\n",__FUNCTION__,__LINE__,server_status);
            break;
        default:
            printf("%s: server_status[%d] not legality\n",__FUNCTION__, server_status);
   }

    return 0;
}

static int demo_cc_upgrade_cb(cc_upgrade_cmd_e cmd, void* data, void* user_data)
{
    cc_upgrade_info *ccinfo = (cc_upgrade_info*)data;
    cc_update_recieve_buf *recive_buf = (cc_update_recieve_buf*)data;
    static cc_uint64 pkg_size = 0;
    
    switch(cmd)
    {
        case CC_CMD_UPGRADE_INFO:
            pkg_size = 0;
            printf("%s line(%d) cmd(%d)=CC_CMD_UPGRADE_INFO,pkg_size=%lld,pkg_type=%d,md5=%s\n",__FUNCTION__,__LINE__,cmd,ccinfo->pkg_size,ccinfo->pkg_type,ccinfo->filemd5);
            printf("pkg_url:%s\n", ccinfo->url);
            break;
        case CC_CMD_UPGRADE_DOWNLOAD:
            pkg_size += recive_buf->size;
            printf("%s line(%d) cmd(%d)=CC_CMD_UPGRADE_DOWNLOAD,size=%lld\n",__FUNCTION__,__LINE__,cmd,recive_buf->size);
            break;
        case CC_CMD_UPGRADE_EXECUTE:
            printf("%s line(%d) cmd(%d)=CC_CMD_UPGRADE_EXECUTE, download size = %lld\n",__FUNCTION__,__LINE__,cmd, pkg_size);
            pkg_size = 0;
            break;
        default:
            printf("%s: cmd[%d] not legality\n",__FUNCTION__, cmd);
    }
    return 0;
}

static int demo_cc_storage_partition_info(cc_storage_partition_info list[], int *max)
{
    int i = 0;
    
    if(list == NULL)    //list==NULL，表示获取分区个数
    {
        *max = 2;
        return 0;
    }
    for(i = 0; i < *max; i++) {
        char name[15] = {0};
        sprintf(name, "/dev/sda%d", i);

        strncpy(list[i].name, name, sizeof(list[i].name));
        strncpy(list[i].fs, "vfat", sizeof(list[i].fs));

        list[i].total = 1024*1024*1024;
        list[i].remain = 500*1024*1024;
    }
    *max = i;
    return 0;
}

static int demo_cc_storage_format()
{
    sleep(10);
    printf("format sdcard ok\n");

    return 0;
}

static int demo_cc_ptz(cc_ptz_cmd_e cmd, void* data, void* user_data)
{    
    switch(cmd)
    {
        case CC_CMD_PTZ_MOVE_ABSOLUTELY:
        {
            cc_space* position = (cc_space*)data;    //坐标值表示需要移动的点的绝对位置
            printf("%s line(%d) cmd(%d)=CC_CMD_PTZ_MOVE_ABSOLUTELY (%f,%f,%f)\n",__FUNCTION__,__LINE__,cmd, position->x, position->y, position->zoom);
            break;
        }
        case CC_CMD_PTZ_MOVE_CONTINUOUSLY:
        {
            cc_space* speed = (cc_space*)data;       //坐标值表示需要移动的方向
            printf("%s line(%d) cmd(%d)=CC_CMD_PTZ_MOVE_CONTINUOUSLY (%f,%f,%f)\n",__FUNCTION__,__LINE__,cmd, speed->x, speed->y, speed->zoom);
            break;
        }
        case CC_CMD_PTZ_GOTO_HOME:
            printf("%s line(%d) cmd(%d)=CC_CMD_PTZ_GOTO_HOME\n",__FUNCTION__,__LINE__,cmd);
            break;
        case CC_CMD_PTZ_GET_POSITION:
        {
            cc_space* position = (cc_space*)data;
            printf("%s line(%d) cmd(%d)=CC_CMD_PTZ_GET_POSITION\n",__FUNCTION__,__LINE__,cmd);
            position->x = 0.5;
            position->y = 0.5;
            position->zoom = 0.0;
            break;
        }
        case CC_CMD_PTZ_MOVE_RELATIVELY:
        {
            cc_space* distance = (cc_space*)data;
            printf("%s line(%d) cmd(%d)=CC_CMD_PTZ_MOVE_RELATIVELY (%f,%f)\n",__FUNCTION__,__LINE__,cmd, distance->x, distance->y);
            break;
        }
        case CC_CMD_PTZ_STOP_MOVE:
            printf("%s line(%d) cmd(%d)=CC_CMD_PTZ_STOP_MOVE\n",__FUNCTION__,__LINE__,cmd);
            break;
        default:
            printf("%s: cmd[%d] not legality\n", __FUNCTION__, cmd);
            break;
    }

    return 0;
}


/*
static void SetThumbnail()
{
    char* buf = NULL;
    int fd = open("/tmp/thumb.jpg", O_RDONLY);
    if(fd < 0) {
        printf("open thumb picture failed\n");
    } else {
        int size = lseek(fd, 0, SEEK_END);
        lseek(fd,0, SEEK_SET);
        buf = (char*) malloc(size);
        if(size != read(fd,buf,size))
        {
            printf("read thumb picture failed, read size=%d\n",size);
            free(buf);
            buf = NULL;
        }
        else
        {
            printf("thumb picture size is %d.\n", size);
            cc_api_set_thumbnail(0, buf, size);
        }
        close(fd);
    }
    if (buf != NULL) {
        free(buf);
    }
}*/



void *demo_stream_pro(void* args)
{
    stream_t *stream = (stream_t *)args;
    LocalFrameHead_t header;
    int header_len = sizeof(LocalFrameHead_t);
    cc_stream_info packet;
    uint32_t video_pts = 0, audio_pts = 0, video_seq = 0, audio_seq = 0;
    static int bfirst_key_frame = 0;
    int ret = -1, length = 0;;
    int size;
    char filepath[256] = {0};
    char cfgdata[1024] = {0};
    uint64_t  llframetime = 0;
    
    bfirst_key_frame = 0;

    printf("demo_stream_pro start ... header_len=%d\n",header_len);

    int fd_read = -1;
    if (stream->source == CC_RAW_SOURCE_H264) {
        strcpy(filepath,H264_FILE_PATH);
    } else if (stream->source == CC_RAW_SOURCE_H265) {
        strcpy(filepath,H265_FILE_PATH);
    } else {
        printf("file source invalid!\n");
        return NULL;
    }
    
    fd_read = open(filepath, O_RDONLY);
    if(fd_read < 0) {
        printf("open file failed,path:%s\n",filepath);
        return NULL;
    }

    unsigned char *rawdata = (unsigned char *)malloc(256*1024);
    while(stream->bcontinue) {
        memset(&packet, 0, sizeof(cc_stream_info));        
        size = read(fd_read, &length, 4);
        if(size < 4) {
            lseek(fd_read, 0, SEEK_SET);
            printf("reopen file again\n");
            continue;
        }
        size = read(fd_read, &header, header_len);
        if (header.type == 3 || header.type == 4) {
            read(fd_read, cfgdata, ntohl(length)-sizeof(header));
            continue;
        }
        size = read(fd_read, rawdata, header.datasize);
        size = read(fd_read, &llframetime, 8);
     //   printf("type: %d, timestamp: %d, size: %d\n",   
     //           header.type, header.starttime, header.datasize);
        
        packet.id = 0;
        packet.channel = 0;
        packet.head = 1;
        packet.tail = 1;
        packet.fisheye = g_support_fisheye == 1 ? 1 : 0; //鱼眼设备设置为1
        packet.refcount = 0;
        packet.payload = rawdata;

        if(header.type == 2) {

            video_pts += 66;
            if(video_pts < audio_pts && audio_pts - video_pts > 1000)
            {
                video_pts = audio_pts;
                printf("adjust video pts\n");
            }
            packet.pts = video_pts;
            packet.utcms = util_current_utc_time_ms();
            packet.sequence = ++video_seq;
            packet.newformat = 0;
            packet.format = CC_STREAM_VIDEO;
            if (stream->source == CC_RAW_SOURCE_H265) {
                packet.video.encode = CC_MEDIA_H265;
                packet.video.width = 1920;
                packet.video.height = 1080;
            } else {
                packet.video.encode = CC_MEDIA_H264;
                packet.video.width = 1280;
                packet.video.height = 720;
            }
            packet.length = header.datasize;
            if(header.sync)
            {
                packet.video.frame = 'I';
                if(!bfirst_key_frame)
                {
                    bfirst_key_frame = 1;
                    packet.newformat = 1;
                }
             //   SetThumbnail();
                printf("set key frame, starttime=%d, systime=%lld,size=%d.\n", 
                        packet.pts, packet.utcms, header.datasize);
            }
            usleep(60000);
        } else if(header.type == 1) {
            audio_pts += 40;
            if(video_pts > audio_pts && video_pts - audio_pts > 1000)
            {
                audio_pts = video_pts;
                printf("adjust audio pts\n");
            }
            packet.pts = audio_pts;
            packet.utcms = util_current_utc_time_ms();
            packet.sequence = ++audio_seq;
            packet.newformat = 0;
            packet.format = CC_STREAM_AUDIO;
            packet.audio.encode = CC_MEDIA_G711A;
            packet.audio.channelcount = 1;
            packet.audio.samplerate = 8000;
            packet.audio.samplebits = 8;
            packet.length = header.datasize;
        }

        if (!bfirst_key_frame) {
            continue;
        }

 //       printf("av_packet write seq: %d, time: %lld, packet_len: %d\n",
 //               packet.sequence, packet.utcms, packet.length);

        ret = cc_api_set_streaming(&packet,0);
        if (ret != 0) {
            bfirst_key_frame = 0;
        }
    }

    free(rawdata);
    close(fd_read);

    return NULL;
}


/*启动塞流线程*/
static int demo_stream_start(stream_t *stream, int source)
{
    printf("Begin___\n");

    stream->bcontinue = true;
    stream->source = source;
    pthread_create(&stream->pid, NULL, demo_stream_pro, stream);

    printf("End___\n");

    return 0;
}


/*停止塞流线程*/
static int demo_stream_stop(stream_t *stream)
{
    printf("Begin___\n");

    stream->bcontinue = false;
    pthread_join(stream->pid, NULL);

    printf("End___\n");

    return 0;
}



/*设置设备基本信息*/
void set_device_info(const char* ifname)
{
    cc_device_info device_info;
    cc_product_info pro_info={{0},{0}};

    printf("set device info ... ifname:%s\n",ifname);
    char mac[20] = {0};
    if(ifname == NULL || *ifname == 0) {
        strcpy(mac,DEVICE_MAC);
    } else {
        util_ifname_mac_address(ifname, mac);
    }
    
    strcpy(device_info.device_type, DEVICE_TYPE);
    strcpy(device_info.chip_name, "hisi");
    strcpy(device_info.app_version, APP_VERSION);
    strcpy(device_info.fw_version, FW_VERSION);
    strcpy(device_info.mac, mac);
    strcpy(device_info.sn, SERIAL_NUMBER); 
    strcpy(pro_info.product_module_id, MODULE_ID);
    strcpy(pro_info.product_name, MODULE_ID);

    cc_api_set_device_info(&device_info, &pro_info);

    return;
}


/*设置设备能力集*/
void set_device_feature()
{
    cc_uint32 feature_count = 0;
    cc_device_feature_e feature[32]={0};

    printf("set device feature ... \n");

    feature[feature_count++] = CC_FEATURE_HAVE_WIFI;
    feature[feature_count++] = CC_FEATURE_HAVE_MIC;   //有MIC，支持语音对讲需要上报此能力
    feature[feature_count++] = CC_FEATURE_HAVE_SPEAKER;   //有扬声器，支持语音对讲需要上报此能力
    feature[feature_count++] = CC_FEATURE_HAVE_LED;
    feature[feature_count++] = CC_FEATURE_HAVE_PIR;
    feature[feature_count++] = CC_FEATURE_HAVE_RING;
    feature[feature_count++] = CC_FEATURE_HAVE_SDCARD;
    feature[feature_count++] = CC_FEATURE_HAVE_PAN;
    feature[feature_count++] = CC_FEATURE_HAVE_TILT;
    feature[feature_count++] = CC_FEATURE_SUPPORT_ROTATE;// 支持旋转，0，180
    feature[feature_count++] = CC_FEATURE_SUPPORT_FULL_DUPLEX_SPEECH;//支持全双工语音对讲
    feature[feature_count++] = CC_FEATURE_SUPPORT_MOTION_DETECTION;//支持运动检测
    feature[feature_count++] = CC_FEATURE_SUPPORT_MOTION_SENSITIVITY;//支持运动检测灵敏度设置
    feature[feature_count++] = CC_FEATURE_SUPPORT_PIR_SENSITIVITY;//支持PIR灵敏度设置
    feature[feature_count++] = CC_FEATURE_SUPPORT_HDVIDEO;//支持高标清切换，HD/SD

    feature[feature_count++] = CC_FEATURE_SUPPORT_NIGHT_VISION;//支持夜视
    feature[feature_count++] = CC_FEATURE_SUPPORT_SPEAKER_VOLUME;//支持扬声器音量调节
    feature[feature_count++] = CC_FEATURE_SUPPORT_SOUND_DETECTION;//支持声音检测
    feature[feature_count++] = CC_FEATURE_SUPPORT_SOUND_SENSITIVITY;//支持声音检测灵敏度设置
    feature[feature_count++] = CC_FEATURE_SUPPORT_PTZ_PRESET;//支持PTZ预置位设置
    feature[feature_count++] = CC_FEATURE_SUPPORT_AUTOTRACKING;//支持运动跟踪
    feature[feature_count++] = CC_FEATURE_SUPPORT_SOUNDTRACKING;//支持声音跟踪    
    feature[feature_count++] = CC_FEATURE_SUPPORT_PEOPLE_DETECTION;//支持人形检测

    cc_api_set_device_feature(feature_count, feature);

    return;
}


/*设置回调函数*/
void set_callback()
{
    printf("set callback ... \n");

    device_callbk.user_data = NULL;    //注意：回调函数结构体device_callbk 不能使用局部变量
    device_callbk.cb_device = demo_cc_device_cb;
    device_callbk.cb_video = demo_cc_video_cb;
    device_callbk.cb_audio = demo_cc_audio_cb;
    device_callbk.cb_network = demo_cc_network_cb;
    device_callbk.cb_server_status = demo_cc_server_status_cb;
    device_callbk.cb_get_feature_default_value = demo_cc_get_feature_default_value_cb;
    device_callbk.cb_get_feature_capacity = demo_cc_get_feature_capacity_cb;
    device_callbk.cb_timeline = NULL;
    device_callbk.cb_storage_partition_info = demo_cc_storage_partition_info;
    device_callbk.cb_storage_format = demo_cc_storage_format;
    device_callbk.cb_media_read = NULL;
    device_callbk.cb_ptz = demo_cc_ptz;
    device_callbk.cb_upgrade = demo_cc_upgrade_cb;
    
    cc_api_set_callback(&device_callbk);

    return;
}

/************************************************************************
功能: 注册设备
@mode:支持按位与的方式，例如mode=3表示同时启动二维码和有线注册方式，
使用说明：启动注册线程后，不同的注册模式，应用程序会收到SDK不同的回调，详情可以参考SDK文档说明。
应用程序需要处理如下回调：
二维码注册：
  - CC_CMD_QR_START：启动QR code扫描并获取YUV数据，YUV420数据，只有Y分量，分辨率不要超过640*480
  - CC_CMD_QR_RELEASE：释放扫描到的YUV数据
  - CC_CMD_QR_STOP ： 停止二维码扫描  
  - CC_CMD_SWITCH_WIFI：连接WIFI
  - CC_REGISTER_SUCCESS/CC_REGISTER_FAIL： 注册成功/失败

AP注册：
 - CC_CMD_SWITCH_WIFI：连接WIFI
 - CC_REGISTER_SUCCESS/CC_REGISTER_FAIL： 注册成功/失败

有线注册
 - CC_REGISTER_SUCCESS/CC_REGISTER_FAIL： 注册成功/失败

注意：应用程序收到注册结果回调时，或者用户主动停止注册时，需要调用cc_stop_register停止
注册线程（不能在回调函数里直接调用）
 ***************************************************************/
int register_device(cc_register_mode_e mode)
{
    int ret = -1;
    int device_status;

    do {
        ret = cc_api_check_register_status(&device_status);

        printf("check register status ret=%x, device_status=%d\n",ret,device_status);
        if(ret == 0) {
            break;
        }
        usleep(200000);
    }while(1);

    if(ret == 0 && !device_status) {
        printf("cc_api_start_register,mode=%d\n",mode);
        usleep(200000);

        ret = cc_api_start_register(mode);  // 1:二维码方式    2： 有线绑定方式     8：AP热点方式
        if(ret != 0) {
            printf("cc_api_start_register err \n");
            return -1;
        }
    }

    return ret;
}


int main(int argc, char* argv[])
{
    printf("____start\n");
    cc_char ca_file[260]={0};
    int connect_flag = 0;
    int opt = -1;
    char ifname[128] = {0};
    int regmode = -1;
    char suspend = 0;
   // char restore = 0;
    int source = 0;   
    char version[10] = {0};
    char username[128] = {0};
    char password[128] = {0};
    
    while ((opt = getopt(argc, argv, "r:c:u:p:i:wf:")) != -1) {
        switch (opt) {
        case 'r':
            regmode=atoi(optarg);
            break;
            
        case 'u':
            strcpy(username,optarg);
            break;

        case 'p':
            strcpy(password, optarg);
            break;
           
        case 'i':
            strcpy(ifname, optarg);
            break;

        case 'c':
            strcpy(ca_file, optarg);
            break;

      /*  case 'w':
            restore = 1;
            break;
*/
        case 'f':
            source = atoi(optarg);
            break;

        default:
            break;
        }
    }

    cc_config_t config;

    config.config_file_path = CONFIGFILEPATH;
    config.ca_file_path = ca_file;
    config.log_path = LOGFILEPATH;
    config.max_buffer_size = 1024 * 1024;  // <流媒体缓冲区大小，720P建议1MB，1080P建议2MB
    config.log_max_line = 5000;            // 默认10000行，最小1000行
    cc_api_get_version(version,sizeof(version));
    printf("sdk version=============================: %s\n", version);

    cc_api_init(&config);
    
    set_device_info(ifname); 
    set_device_feature();
    set_callback();

    int ret = cc_api_start();
    if(ret < 0) {
        printf("start error, ret: %d!\n", ret);
    }

    if(regmode>=0) {
        sleep(1);
        if(regmode == 0)  //直接注册，仅用于demo
        {
            cc_register_info info;            
            strcpy(info.username,username);
            strcpy(info.password,password);
            strcpy(info.qkey,QRCODE_KEY);
            strcpy(info.locale,LOCALE);               
            ret = cc_api_direct_register(info);
            printf("regist camera, ret=%d\n",ret);
        }
        else
        {
            register_device(regmode);
        }
    }

    demo_stream_start(&g_stream, source);   //启动塞流线程

    while(1) {
        int option=0;
        printf("please input cmd:\n  1:sdcard unplug  \n");
        printf("  2:sdcard plugin  \n");
        printf("  3:motion start   \n");
        printf("  4:motion end     \n");
        printf("  5:goto sleep\n");
        printf("  6:ring wakeup\n");
        printf("  7:pir wakeup\n");

        scanf("%d",&option);
        switch(option) {
        case 1:
        {
            printf("sdcard plug out\n");
            cc_event_attribute attr;
            attr.sdcard_value.status = (int)SDCARD_STATUS_NO_SDCARD;    //1:no sdcard  2:sdcard abnormal  3:sdcard normal
            strcpy(attr.sdcard_value.path, "");
            cc_api_set_event(0, CC_DETECT_SDCARD, CC_DETECT_PULSE, &attr);
            break;
        }
        case 2:
        {
            printf("sdcard plug in\n");
            cc_event_attribute attr;
            attr.sdcard_value.status = (int)SDCARD_STATUS_NORMAL;    //1:no sdcard  2:sdcard abnormal  3:sdcard normal
            strcpy(attr.sdcard_value.path, "/tmp");
            cc_api_set_event(0, CC_DETECT_SDCARD, CC_DETECT_PULSE, &attr);
            break;
        }
        case 3:
        {
            printf("motion start\n");
            cc_api_set_event(util_current_utc_time_ms(), CC_DETECT_MOTION, CC_DETECT_START, NULL);
            break;
        }
        case 4:
        {
            printf("motion end\n");
            cc_api_set_event(util_current_utc_time_ms(), CC_DETECT_MOTION, CC_DETECT_STOP, NULL);
            break;
        }
        case 5:        //低功耗设备，进入休眠状态
        {
            printf("go to sleep\n");            
            demo_sleep();
            break;
        }
        case 6:         
        {
            printf("ring wake up\n");
            demo_wakeup(CC_WAKE_UP_BY_RING);
            break;
        }
        case 7:         
        {
            printf("pir wake up\n");
            demo_wakeup(CC_WAKE_UP_BY_PIR);
            break;
        }
        case 8:
        {
            printf("ipc status change\n");  //中继（一拖一）子设备状态上报
            cc_event_attribute attr;
            attr.doorbell_status.status = DOORBELL_STATUS_SLEEP;
            cc_api_set_event(util_current_utc_time_ms(), CC_DETECT_DOORBELL_STATUS, CC_DETECT_PULSE, &attr);
            break;
        }
        case 9:
        {
            printf("ipc status change\n");  //中继（一拖一）子设备状态上报
            cc_event_attribute attr;
            attr.doorbell_status.status = DOORBELL_STATUS_ONLINE;
            cc_api_set_event(util_current_utc_time_ms(), CC_DETECT_DOORBELL_STATUS, CC_DETECT_PULSE, &attr);
            break;
        }
        case 10:
        {
            printf("ipc status change\n");  //中继（一拖一）子设备状态上报
            cc_event_attribute attr;
            attr.doorbell_status.status = DOORBELL_STATUS_OFFLINE;
            cc_api_set_event(util_current_utc_time_ms(), CC_DETECT_DOORBELL_STATUS, CC_DETECT_PULSE, &attr);
            break;
        }
        default:
            printf("invalid cmd\n");
            break;
        }
    }

    connect_flag = 1;

    while(connect_flag) {
        if (suspend) {
            break;
        }
        sleep(1);
    }

    demo_stream_stop(&g_stream);

    cc_api_stop();

    //uninit
    //cc_uninit();

 //   if (suspend)
 //       wifi_heartbeat(hbconfig);

    return 0;
}
